/*
 * Copyright 2003-2005 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

#include "Types.h"

#include "TypeMap.h"

#include "TypeHelper.h"

#include "util/Buffer.h"

#include "meta/MetaDefinition.h"

static struct type_map_vtable typeMapVtable =
{
	&_type_map_map,
	&_type_map_get_id,
	&_type_map_get_name,
	&_type_map_get_reader,
	&_type_map_get_writer,
	&_type_map_get_structure,
	&_type_map_get_library_id,
	&_type_map_get_mapped_id,
	&_type_map_is_valid_id,
	&_type_map_is_valid_name,
	&_type_map_get_size
};

void type_map_init( type_map * map, type_library * library )
{
	map->__vt = &typeMapVtable;
	map->library = library;
	intmap_init( &map->map, 4 );
}

void type_map_destroy( type_map * map )
{
	
}

int _type_map_map( type_map * map, int id, int systemid )
{
	if ( systemid <= NOTYPE  )
		return TYPE_INVALID_PARAM;
		
	intmap_add( &map->map, id,  systemid );	
	return TYPE_OK;
}

int _type_map_get_id( type_map * map, type_name * name )
{
	int i = type_library_get_id( map->library, name );
	if ( i <= NOTYPE )
	{
		return i;
	}
	
	int id = intmap_find_key( &map->map, i );
	if ( id != TYPE_OK )
		return TYPE_NOT_MAPPED;

	return intmap_key( &map->map );
}

type_name * _type_map_get_name( type_map * map, int id )
{
	int libraryId = type_map_get_library_id( map, id );
	if ( libraryId <= NOTYPE )
		return NULL;
	
	return type_library_get_name( map->library, libraryId );
}

typeReader _type_map_get_reader( type_map * map, int id )
{
	int libraryId = type_map_get_library_id( map, id );
	if ( libraryId <= NOTYPE )
		return NULL;
	
	return type_library_get_reader( map->library, libraryId );
}

typeWriter _type_map_get_writer( type_map * map, int id )
{
	int libraryId = type_map_get_library_id( map, id );
	if ( libraryId <= NOTYPE )
		return NULL;
	
	return type_library_get_writer( map->library, libraryId );
}

type_element * _type_map_get_structure( type_map * map, int id )
{
	int libraryId = type_map_get_library_id( map, id );
	if ( libraryId <= NOTYPE )
		return NULL;
	
	return type_library_get_structure( map->library, libraryId );
}

int _type_map_get_library_id( type_map * map, int id )
{
	if ( id <= NOTYPE )
		return NOTYPE;
	
	int i = intmap_find( &map->map, id );
	if ( i <= -1 )
		return TYPE_NOT_MAPPED;
		
	return intmap_value( &map->map );	
}

int _type_map_get_mapped_id( type_map * map, int systemId )
{
	if ( systemId <= NOTYPE )
		return TYPE_INVALID_PARAM;
	
	int i = intmap_find_key( &map->map, systemId );		
	if ( i <= NOTYPE )
		return TYPE_NOT_MAPPED;
	
	return intmap_key( &map->map );
}

bool _type_map_is_valid_id( type_map * map, int id )
{
	int i = intmap_find( &map->map, id );
	if ( i <= NOTYPE )
		return false;
				
	return true;
}

bool _type_map_is_valid_name( type_map * map, type_name * name )
{
	int i = type_library_get_id( map->library, name );
	if ( i <= NOTYPE )
		return false;
	
	int mid = intmap_find_key( &map->map, i );
	if ( mid <= NOTYPE )
		return false;
				
	return true;
}

int _type_map_get_size( type_map * map )
{
	return intmap_size( &map->map );
}


bool type_map_is_same( type_map * map, int id, type_name * name, type_buffer * structure, reference_type_map * coreMap )
{
	/* First check if we can find the same identifier. */
	int i;
	
	i = type_library_get_id( map->library, name);

		
	/* Are the identifiers the same. */
	if ( id != i )
	{
		return false;
	}

	/* write the local version out using the same map and compare. */	
	type_element * localStruct = type_library_get_structure( map->library, i );
	
	return type_helper_structure_matches( coreMap, localStruct, structure );
}


type_element * type_map_read_structure( type_map * map, reference_type_map * core, type_buffer * structure )
{
	buffer_data reader_data;
	type_input_stream tmis;
	int rc;
	
	/* Get hold of the type map required to read the structure. */
	buffer_data_init( &reader_data, structure->data, structure->size );
	type_input_init( &tmis, &core->map, bufferReader, &reader_data );
	
	/* Read the object
	 * We need to read the object and resolve all the types to
	 * the local identifiers.  After that we can write out the
	 * structure in codes the host understands, then compare
	 * the result with the internal version we already have.
	 * We could have done a tree walker over the types, but
	 * this is easier.
	 */
	 
	rc = type_input_read_named_object( &tmis, &meta_definition_name );
	if ( rc != TYPE_OK ) return NULL;
	type_element * definition = (type_element *) type_stack_get_ptr( type_input_stack( &tmis ) );
	return definition;
}

void type_map_iterator_init( type_map_iterator * iter, type_map * map )
{
	intmap_iterator_init( &iter->iter, &map->map );
}

bool type_map_iterator_next( type_map_iterator * iter )
{
	return intmap_iterator_next( &iter->iter );
}

int type_map_iterator_id( type_map_iterator * iter )
{
	return intmap_iterator_key( &iter->iter );
}


